package com.wb3.meta.api.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.WriterException;
import com.google.zxing.client.j2se.MatrixToImageWriter;
import com.google.zxing.common.BitMatrix;
import com.google.zxing.qrcode.QRCodeWriter;
import com.wb3.meta.api.core.dao.GoodsInfoMapper;
import com.wb3.meta.api.core.model.GoodsInfo;
import com.wb3.meta.api.core.service.GoodsInfoService;
import com.wb3.meta.api.core.service.GoodsOrdersService;
import com.wb3.meta.api.core.service.MyToysService;
import com.wb3.meta.api.core.vo.ReqGoodsInfoVo;
import com.wb3.meta.api.core.vo.ReqGoodsOrdersVo;
import com.wb3.meta.api.core.vo.ResGoodsInfoVo;
import com.wb3.meta.api.core.vo.ResGoodsOrdersVo;
import com.wb3.meta.common.authentication.RequestHolder;
import com.wb3.meta.common.enums.EnumConstants;
import com.wb3.meta.common.mybatis.DataTableUtil;
import com.wb3.meta.common.pojo.ResultT;
import com.wb3.meta.config.utils.WxPayUtil;
import com.wechat.pay.java.core.Config;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.service.payments.nativepay.NativePayService;
import com.wechat.pay.java.service.payments.nativepay.model.Amount;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayRequest;
import com.wechat.pay.java.service.payments.nativepay.model.PrepayResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.security.NoSuchAlgorithmException;
import java.util.Date;
import java.util.List;
import java.util.Map;

@Slf4j
@Service("goodsInfoService")
@Transactional(transactionManager = "dataSourceTransactionManagerOne", rollbackFor = Exception.class)
public class GoodsInfoServiceImpl implements GoodsInfoService {

    @Autowired
    private GoodsInfoMapper goodsInfoMapper;

    @Autowired
    private WxPayUtil wxPayUtil;
    @Autowired
    private WxPayService wxService;

    @Autowired
    private GoodsOrdersService goodsOrdersService;

    @Autowired
    private MyToysService myToysService;


    @Value("${wx.pay.mchId}")
    private String mchId;
    @Value("${wx.pay.privateKeyPathLocal}")
    private String privateKeyPathLocal;
    @Value("${wx.pay.merchantSerialNumber}")
    private String merchantSerialNumber;
    @Value("${wx.pay.apiV3key}")
    private String apiV3key;
    @Value("${wx.pay.appId}")
    private String appId;
    @Value("${wx.pay.notifyUrl}")
    private String notifyUrl;

    @Value("classpath:cert/apiclient_key.pem")
    private Resource resource;

    /**
     * 分页查询方法
     *
     * @param vo
     * @return
     */
    @Override
    public Page<ResGoodsInfoVo> getModelPage(ReqGoodsInfoVo vo) {

        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());

        return (Page<ResGoodsInfoVo>) getModelListByConditions(vo);
    }

    /**
     * 列表查询方法 -返回 res 对象
     *
     * @param vo
     * @return
     */
    @Override
    public List<ResGoodsInfoVo> getModelListByConditions(ReqGoodsInfoVo vo) {
        return goodsInfoMapper.getModelListByConditions(vo);
    }

    /**
     * 根据条件查询单条数据方法，返回res vo类
     *
     * @param vo
     * @return
     */
    @Override
    public ResGoodsInfoVo getResModelByConditions(ReqGoodsInfoVo vo) {
        return goodsInfoMapper.getResModelByConditions(vo);
    }

    /**
     * 根据条件查询单个对象方法
     *
     * @param goodsInfo
     * @return
     */
    @Override
    public GoodsInfo getModelByConditions(GoodsInfo goodsInfo) {
        return null;
    }

    /**
     * 根据id查询单个对象方法
     *
     * @param id
     * @return
     */
    @Override
    public GoodsInfo getModelById(Object id) {
        return goodsInfoMapper.selectByPrimaryKey(id);
    }

    /**
     * 新增对象方法
     *
     * @param goodsInfo
     */
    @Override
    public void addModel(GoodsInfo goodsInfo) {
        goodsInfo.setCreateTime(new Date());
        goodsInfo.setStatus(EnumConstants.StatusEnum.VALID.getCode());
        goodsInfoMapper.insertUseGeneratedKeys(goodsInfo);
    }

    /**
     * 更新对象方法
     *
     * @param goodsInfo
     */
    @Override
    public void updateModel(GoodsInfo goodsInfo) {
        goodsInfo.setUpdateTime(new Date());
        goodsInfoMapper.updateByPrimaryKeySelective(goodsInfo);
    }

    /**
     * 根据id删除对象方法
     *
     * @param id
     */
    @Override
    public void deleteModelById(Object id) {

    }

    /**
     * 获取分页数据，返回 map对象方法
     *
     * @param vo
     * @return
     */
    @Override
    public ResultT getModelMapPage(ReqGoodsInfoVo vo) {
        return ResultT.success(DataTableUtil.changePage(getModelPage(vo)));
    }

    /**
     * 根据id查询单个对象方法，返回 map对象方法
     *
     * @param id
     * @return
     */
    @Override
    public ResultT getModelMapById(Object id) {
        return null;
    }

    /**
     * 根据条件查询单个对象，返回map对象方法
     *
     * @param goodsInfo
     * @return
     */
    @Override
    public ResultT getModelMapByConditions(GoodsInfo goodsInfo) {
        return null;
    }

    /**
     * 查询列表-map对象
     *
     * @param vo
     * @return
     */
    @Override
    public ResultT getResModelListMapByConditions(ReqGoodsInfoVo vo) {
        return ResultT.success(goodsInfoMapper.getModelListByConditions(vo));
    }

    /**
     * 根据条件查询res vo对象，返回map对象方法
     *
     * @param vo
     * @return
     */
    @Override
    public ResultT getResModelMapByConditions(ReqGoodsInfoVo vo) {
        return ResultT.success(getResModelByConditions(vo));
    }

    /**
     * 更新或插入对象方法
     *
     * @param vo
     * @return
     */
    @Override
    public ResultT saveOrUpdate(ReqGoodsInfoVo vo) {
        if (vo.getId() != null) {
            updateModel(vo);
        } else {
            addModel(vo);
        }
        return ResultT.success(vo);
    }

    /**
     * 根据条件删除方法
     *
     * @param vo
     * @return
     */
    @Override
    public ResultT delete(ReqGoodsInfoVo vo) {
        return null;
    }


    @Override
    public ResultT buyCheckBefore(ReqGoodsInfoVo reqGoodsInfoVo) {
//        if (reqGoodsInfoVo.getId() == null) {
//            return ResultT.fail("商品id不能为空");
//        }
//        GoodsInfo goodsInfo = getModelById(reqGoodsInfoVo.getId());
//        if (goodsInfo == null) {
//            return ResultT.fail("未查询到商品");
//        }
//        if (goodsInfo.getStatus() != 2) {
//            return ResultT.fail("商品非销售状态");
//        }
//        if (goodsInfo.getSaleTime().getTime() < new Date().getTime()) {
//            return ResultT.fail("未到销售时间");
//        }
//        if (goodsInfo.getQuantity() <= 0) {
//            return ResultT.fail("商品已售罄");
//        }
        return ResultT.success();
    }

    @Override
    public ResultT createOrder(ReqGoodsOrdersVo reqGoodsOrdersVo) throws WxPayException, NoSuchAlgorithmException {


        if (StringUtils.isBlank(reqGoodsOrdersVo.getOrderNo())) {
            return ResultT.fail("订单号不能为空");
        }
        ResGoodsOrdersVo resGoodsOrdersVo = goodsOrdersService.getResModelByConditions(reqGoodsOrdersVo);
        if (resGoodsOrdersVo == null) {
            return ResultT.fail("订单不存在");
        }
        if (resGoodsOrdersVo.getStatus() != 0) {
            return ResultT.fail("订单无法继续支付");
        }
        WxPayUnifiedOrderRequest wxPayUnifiedOrderRequest = new WxPayUnifiedOrderRequest();
        wxPayUnifiedOrderRequest.setBody(generateProductDesc(resGoodsOrdersVo));
        wxPayUnifiedOrderRequest.setOutTradeNo(reqGoodsOrdersVo.getOrderNo());
        wxPayUnifiedOrderRequest.setTotalFee(calTotalPrice(resGoodsOrdersVo.getBuyPrice()));
        wxPayUnifiedOrderRequest.setSpbillCreateIp(StringUtils.isNotBlank(reqGoodsOrdersVo.getIp()) ? reqGoodsOrdersVo.getIp() : "127.0.0.1");
//        wxPayUnifiedOrderRequest.setNotifyUrl("payCallBack");
        wxPayUnifiedOrderRequest.setTradeType("JSAPI");
        wxPayUnifiedOrderRequest.setOpenid(RequestHolder.currentUser().getOpenId());
        WxPayUnifiedOrderResult orderResult = wxService.unifiedOrder(wxPayUnifiedOrderRequest);
        Map<String, String> stringMap = wxPayUtil.convertWXPay(orderResult);
        stringMap.put("outTradeNo", reqGoodsOrdersVo.getOrderNo());


        return ResultT.success(stringMap);
    }

    @Override
    public void handleNofityResult(String xmlData) throws WxPayException {
        WxPayOrderNotifyResult notifyResult = wxService.parseOrderNotifyResult(xmlData);
        ReqGoodsOrdersVo reqGoodsOrdersVo = new ReqGoodsOrdersVo();
        reqGoodsOrdersVo.setOrderNo(notifyResult.getOutTradeNo());
        ResGoodsOrdersVo resGoodsOrdersVo = goodsOrdersService.getResModelByConditions(reqGoodsOrdersVo);
        if (resGoodsOrdersVo == null) {
            log.error("崽崽订单号：{}，未在数据库中查询到，本次购买回调执行结束", notifyResult.getOutTradeNo());
            return;
        }
        reqGoodsOrdersVo.setId(resGoodsOrdersVo.getId());
        reqGoodsOrdersVo.setWxOrderId(notifyResult.getTransactionId());
        if (notifyResult.getResultCode().equals("SUCCESS")) {
            reqGoodsOrdersVo.setStatus(2); //支付成功;
            //如果购买的是崽崽，则要给用户添加崽崽
            if (resGoodsOrdersVo.getType() == 0) {
                myToysService.handleBuyToys(resGoodsOrdersVo);
            }
            //更新商品剩余数量 和已售卖数量
            ReqGoodsInfoVo reqGoodsInfoVo = new ReqGoodsInfoVo();
            reqGoodsInfoVo.setId(resGoodsOrdersVo.getGoodsId());
            ResGoodsInfoVo resGoodsInfoVo = getResModelByConditions(reqGoodsInfoVo);
            BeanUtils.copyProperties(resGoodsInfoVo, reqGoodsInfoVo);
            reqGoodsInfoVo.setQuantity(resGoodsInfoVo.getQuantity() - resGoodsOrdersVo.getBuyQty());
            int saledQty = resGoodsInfoVo.getSaledQty() == null ? 0 : resGoodsInfoVo.getSaledQty();
            reqGoodsInfoVo.setSaledQty(saledQty + resGoodsOrdersVo.getBuyQty());
            saveOrUpdate(reqGoodsInfoVo);

        } else {
            reqGoodsOrdersVo.setStatus(3); //支付失败;
        }
        String json = JSON.toJSONString(notifyResult);
        reqGoodsOrdersVo.setWxRespData(json);
        log.info("订单号：{}，微信支付回调信息：{}", notifyResult.getOutTradeNo(), json);
        goodsOrdersService.saveOrUpdate(reqGoodsOrdersVo);

    }

    @Override
    public ResultT createNativeOrder(ReqGoodsOrdersVo reqGoodsOrdersVo) {
        if (StringUtils.isBlank(reqGoodsOrdersVo.getOrderNo())) {
            return ResultT.fail("订单号不能为空");
        }
        ResGoodsOrdersVo resGoodsOrdersVo = goodsOrdersService.getResModelByConditions(reqGoodsOrdersVo);
        if (resGoodsOrdersVo == null) {
            return ResultT.fail("订单不存在");
        }
        if (resGoodsOrdersVo.getStatus() != 0) {
            return ResultT.fail("订单无法继续支付");
        }

        InputStream inputStream  = null;
        try {
            inputStream = resource.getInputStream();
        }catch (IOException e){
            return ResultT.fail("读取证书失败");
        }
        String privateKey = "";
        try {
            privateKey = IOUtils.toString(inputStream);
        }catch (IOException e){
            return ResultT.fail("读取证书失败");
        }
        Config config =
                new RSAAutoCertificateConfig.Builder()
                        .merchantId(mchId)
                        .privateKey(privateKey)
//                        .privateKeyFromPath(privateKeyPathLocal)
                        .merchantSerialNumber(merchantSerialNumber)
                        .apiV3Key(apiV3key)
                        .build();
        // 构建service
        NativePayService service = new NativePayService.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest request = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(100);
        request.setAmount(amount);
        request.setAppid(appId);
        request.setMchid(mchId);
        request.setDescription(generateProductDesc(resGoodsOrdersVo));
        request.setNotifyUrl(notifyUrl);
        request.setOutTradeNo(resGoodsOrdersVo.getOrderNo());
        // 调用下单方法，得到应答
        PrepayResponse response = null;
        try {
            response = service.prepay(request);
        } catch (Exception e) {
            log.error("订单号：{}，创建订单失败", reqGoodsOrdersVo.getOrderNo(), e);
            return ResultT.fail("订单创建失败");
        }
        String base64Image = "data:image/png;base64,";
        try {
            byte[] qrCodeBytes = generateQRCode(response.getCodeUrl());
            base64Image += Base64.encodeBase64String(qrCodeBytes);
            log.info("QR Code URL: " + response.getCodeUrl());
            log.info("订单号：{}，生成qrcode： {}", reqGoodsOrdersVo.getOrderNo(),base64Image);
            reqGoodsOrdersVo.setPayQrcodeUrl(base64Image);
            reqGoodsOrdersVo.setId(resGoodsOrdersVo.getId());
            goodsOrdersService.saveOrUpdate(reqGoodsOrdersVo);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("订单号：{}，生成支付二维码失败", reqGoodsOrdersVo.getOrderNo(), e);
            return ResultT.fail("生成支付二维码失败");
        }
        return ResultT.success(base64Image);
    }

    private static byte[] generateQRCode(String url) throws WriterException, IOException {
        QRCodeWriter writer = new QRCodeWriter();
        BitMatrix bitMatrix = writer.encode(url, BarcodeFormat.QR_CODE, 200, 200);
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        MatrixToImageWriter.writeToStream(bitMatrix, "PNG", outputStream);
        return outputStream.toByteArray();
    }

    private int calTotalPrice(BigDecimal price) {
        BigDecimal result = price.multiply(new BigDecimal(100));
        return result.intValue();
    }

    private String generateProductDesc(ResGoodsOrdersVo resGoodsOrdersVo) {
        return "超级崽崽-" + (resGoodsOrdersVo.getOrderType() == 0 ? "购买 " : "续费 ") + "[" + resGoodsOrdersVo.getGoodsName() + "]";
    }

}
